<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>JVM 调优理论 | 王开琦的个人小站</title><meta name="author" content="王开琦"><meta name="copyright" content="王开琦"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="JVM 内存结构JVM 内存大致分为了五块区域 ，分别是堆（Heap）、虚拟机栈（Java Stack）、本地方法栈(Native Method Stack)、程序计数器（Program Counter Register）、方法区（Method Area） 所有线程都会共享堆内存和方法区；每个线程都会拥有自己独立的虚拟机栈、本地方法栈、程序计数器。  堆堆是JVM内存中最大的一块存储空间  ，绝大">
<meta property="og:type" content="article">
<meta property="og:title" content="JVM 调优理论">
<meta property="og:url" content="https://blog.wkq.pub/blog/1-Java-JVM%E8%B0%83%E4%BC%98%E7%90%86%E8%AE%BA/index.html">
<meta property="og:site_name" content="王开琦的个人小站">
<meta property="og:description" content="JVM 内存结构JVM 内存大致分为了五块区域 ，分别是堆（Heap）、虚拟机栈（Java Stack）、本地方法栈(Native Method Stack)、程序计数器（Program Counter Register）、方法区（Method Area） 所有线程都会共享堆内存和方法区；每个线程都会拥有自己独立的虚拟机栈、本地方法栈、程序计数器。  堆堆是JVM内存中最大的一块存储空间  ，绝大">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://blog.wkq.pub/media/top_img.jpg">
<meta property="article:published_time" content="2022-04-21T06:40:41.000Z">
<meta property="article:modified_time" content="2022-04-22T15:05:06.178Z">
<meta property="article:author" content="王开琦">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://blog.wkq.pub/media/top_img.jpg"><link rel="shortcut icon" href="/media/favicon.ico"><link rel="canonical" href="https://blog.wkq.pub/blog/1-Java-JVM%E8%B0%83%E4%BC%98%E7%90%86%E8%AE%BA/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"簡"},
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    jQuery: 'https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js',
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/js/jquery.justifiedGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/css/justifiedGallery.min.css'
    },
    fancybox: {
      js: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.js',
      css: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.css'
    }
  },
  isPhotoFigcaption: true,
  islazyload: true,
  isanchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'JVM 调优理论',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-04-22 23:05:06'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if (GLOBAL_CONFIG_SITE.isHome && /iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    document.addEventListener('pjax:complete', detectApple)})(window)</script><meta name="generator" content="Hexo 5.2.0"></head><body><div id="loading-box"><div class="loading-left-bg"></div><div class="loading-right-bg"></div><div class="spinner-box"><div class="configure-border-1"><div class="configure-core"></div></div><div class="configure-border-2"><div class="configure-core"></div></div><div class="loading-word">加载中...</div></div></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src= "" data-lazy-src="/media/avatar.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data"><div class="data-item is-center"><div class="data-item-link"><a href="/archives/"><div class="headline">文章</div><div class="length-num">15</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/tags/"><div class="headline">标签</div><div class="length-num">17</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/categories/"><div class="headline">分类</div><div class="length-num">15</div></a></div></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('/media/top_img.jpg')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">王开琦的个人小站</a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">JVM 调优理论</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-04-21T06:40:41.000Z" title="发表于 2022-04-21 14:40:41">2022-04-21</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-04-22T15:05:06.178Z" title="更新于 2022-04-22 23:05:06">2022-04-22</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Java/">Java</a><i class="fas fa-angle-right post-meta-separator"></i><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Java/JVM/">JVM</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">6k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>21分钟</span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h2 id="JVM-内存结构"><a href="#JVM-内存结构" class="headerlink" title="JVM 内存结构"></a>JVM 内存结构</h2><p>JVM 内存大致分为了五块区域 ，分别是堆（Heap）、虚拟机栈（Java Stack）、本地方法栈(Native Method Stack)、程序计数器（Program Counter Register）、方法区（Method Area）</p>
<p>所有线程都会共享堆内存和方法区；每个线程都会拥有自己独立的虚拟机栈、本地方法栈、程序计数器。</p>
<p><img src= "" data-lazy-src="JVM内存结构.png" alt=""></p>
<h3 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h3><p>堆是JVM内存中最大的一块存储空间  ，绝大部分对象都是保存在堆内存中的。</p>
<p>堆内存又做了细分，元空间并不是堆内存的一部分，而是本地内存。</p>
<p><img src= "" data-lazy-src="堆内存划分.png" alt=""></p>
<p>元空间的大小取决于操作系统能够分配的内存大小，这样就解决了持久代空间难以分配的问题。</p>
<h3 id="虚拟机栈"><a href="#虚拟机栈" class="headerlink" title="虚拟机栈"></a>虚拟机栈</h3><p>虚拟机栈是线程独享的 ，当创建一个线程的时候就会创建一个虚拟机栈。虚拟机栈由栈帧组成，每一次的方法调用都会创建一个栈帧，然后入栈，当方法返回的时候则对应着栈帧的出栈操作。</p>
<p><img src= "" data-lazy-src="虚拟机栈.png" alt=""></p>
<p>方法里面的代码在执行的时候会从局部变量表或者对象实例的字段里面复制变量或者是常量放到操作数栈里 ，当计算的时候会使用一系列的指令往操作数栈里面 放入数据或者取走数据进行计算，操作数栈是用来存放临时数据的地方。</p>
<h3 id="本地方法栈"><a href="#本地方法栈" class="headerlink" title="本地方法栈"></a>本地方法栈</h3><p>本地方法栈和虚拟机栈的功能是类似的，虚拟机栈管理 Java 方法 ，本地方法栈管理 Native 方法（本地方法都是由 C 语言去实现的）</p>
<h3 id="程序计数器"><a href="#程序计数器" class="headerlink" title="程序计数器"></a>程序计数器</h3><p>程序计数器用来记录各个线程执行的字节码的地址，诸如 分支、循环、跳转、异常、线程恢复等等操作，都需要依赖程序计数器。由于Java 是多线程的语言，当执行的线程数量超过CPU核心的时候，线程之间就会根据时间片去争抢CPU资源，所以需要给每一个线程分配一个程序计数器，用来记录下一条要执行的指令是什么。</p>
<h3 id="方法区"><a href="#方法区" class="headerlink" title="方法区"></a>方法区</h3><p><strong>方法区并不是一个物理上的区域，而是一个逻辑上的划分</strong></p>
<p>方法区也是多个线程共享的 ,主要包括四部分，类信息（静态常量池）、运行时常量池、字符串常量池、静态变量，方法区主要用来存放虚拟机加载的类相关的信息</p>
<p><img src= "" data-lazy-src="方法区.png" alt=""></p>
<div class="note primary simple"><p>常量池—静态常量池</p>
</div>
<p>也叫做class文件常量池，主要存放：</p>
<ul>
<li>字面量：例如文本字符串、final修饰的常量</li>
<li>符号引用：例如类和接口的全限定名、字符的名称和描述符、方法的名称和描述符</li>
</ul>
<div class="note primary simple"><p>常量池—运行时常量池</p>
</div>
<p>当类加载到内存中后，JVM 就会将静态常量池中的内容存放到运行时的常量池中；运行时常量池里面存储的主要是编译期间生成的字面量、符号引用等等。</p>
<div class="note primary simple"><p>常量池—字符串常量池</p>
</div>
<p>字符串常量池，可以理解为是运行时常量池中分出来的一部分，类加载到内存的时候，字符串，会存到字符串常量池里面。</p>
<p>方法区和堆之间是存在交集的，静态变量、字符串常量池存储在堆里；而类的相关信息以及运行时常量池存储在元空间里面。</p>
<div class="note default simple"><p>示例</p>
</div>
<p>以下代码，在启动的时候，先把类加载到方法区；</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JVMTest1</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//执行这句的时候，首先会在栈中创建一个局部变量 demo，这个 demo 变量会指向一个引用。而真正的demo对象存储在堆中</span></span><br><span class="line">        Demo demo = <span class="keyword">new</span> Demo(<span class="string">&quot;wkq&quot;</span>);</span><br><span class="line">        <span class="comment">//执行每一个方法的时候都会有一个压栈和出栈的操作；调用这个方法的时候会创建一个栈帧并将栈帧入栈；当这行代码返回的时候，栈帧又会从栈里面弹出来。</span></span><br><span class="line">        demo.printName();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Demo</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Demo</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">printName</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="keyword">this</span>.name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><img src= "" data-lazy-src="JVMTest1内存分配.png" alt=""></p>
<h2 id="类加载过程详解"><a href="#类加载过程详解" class="headerlink" title="类加载过程详解"></a>类加载过程详解</h2><p>编写的Java 代码是不能直接运行的，是需要变成 class 文件才可以，使用JDK 内置的 javac 命令实现；</p>
<p><img src= "" data-lazy-src="类加载过程详解.png" alt=""></p>
<ul>
<li><p>加载：当一个类被创建实例或者被引用到的时候，如果虚拟机发现之前没有加载过这个类，就会通过类加载器 classLoader 把 class 文件加载到内存，在加载的过程中主要做了三件事：</p>
<ul>
<li><p>读取类的二进制流</p>
</li>
<li><p>把二进制流转为方法区数据结构，并存放到方法区</p>
</li>
<li><p>在Java堆中产生 java.lang.Class 对象</p>
</li>
</ul>
</li>
<li><p>链接：加载完成之后就会进入链接的步骤，链接这个步骤又可以细分为：</p>
<ul>
<li><p>验证：验证class文件是不是符合规范，(是否正常)</p>
<ul>
<li>文件格式的验证<ul>
<li>是否是 0xCAFEBABE开头，可以打开十六进制编辑器打开查看</li>
<li>版本号是否合理</li>
</ul>
</li>
<li>元数据验证<ul>
<li>是否有父类</li>
<li>是否继承了final类（final类不能被继承，如果继承了就有问题了。）</li>
<li>非抽象类实现了所有抽象方法</li>
</ul>
</li>
<li><p>字节码验证</p>
<ul>
<li>运行检查</li>
<li>栈数据类型和操作码操作参数吻合（比如栈空间只有2字节，但其实却需要大于2字节，此时就认为这个字节码是有问题的）</li>
<li>跳转指令指向合理的位置</li>
</ul>
</li>
<li><p>符号引用验证</p>
<ul>
<li>常量池中描述类是否存在</li>
<li>访问的方法或字段是否存在且有足够的权限</li>
</ul>
</li>
<li><p>如果确认代码是准确无误的，可以在启动的时候添加  <code>-Xverify:none</code> 关闭验证，从而加快类的加载</p>
</li>
</ul>
</li>
<li><p>准备：如果通过验证，就会进入准备环节，作用是为类的静态变量分配内存，初始化为系统的初始值</p>
<ul>
<li>final static 修饰的变量： 直接赋值为用户定义的值，比如 private final static int value =123，直接赋值123</li>
<li>private static int value=123，该阶段的值依然是 0</li>
</ul>
</li>
<li><p>解析： 符号引用转换为直接引用，所谓的符号引用就是 在编译期 Java 类还不知道引用的对象的实际地址，只能使用一个符号 。</p>
</li>
</ul>
</li>
<li><p>初始化</p>
<ul>
<li><p>执行 <code>&lt;class init&gt;</code>方法，clinit方法由编译器自动收集类里面的所有静态变量的赋值动作及静态语句块合并而成，也叫类构造器方法</p>
<ul>
<li><p>初始化的顺序和源文件中的顺序一致</p>
<p>实例一：运行结果为1</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JVMTest3</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        i = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        System.out.println(i);<span class="comment">//1</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>示例二：运行结果为0</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JVMTest3</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        i = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        System.out.println(i);<span class="comment">//0</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>子类的<code>&lt;clinit&gt;</code>被调用前，会先调用父类的<code>&lt;clinit&gt;</code></p>
</li>
<li><p>JVM会保证 clinit 方法的线程安全性</p>
</li>
</ul>
</li>
<li><p>初始化时，如果实例化一个新对象，会调用 <code>&lt;init&gt;</code>方法对实例变量进行初始化，并执行对应的构造方法内的代码。</p>
<ul>
<li>构造块执行的顺序在构造方法之前</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> theory;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JVMTest5</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;JVMTest5静态块&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;JVMTest5构造块&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">JVMTest5</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;JVMTest5构造方法&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *JVMTest5静态块</span></span><br><span class="line"><span class="comment">     * Super 静态块</span></span><br><span class="line"><span class="comment">     * Sub 静态块</span></span><br><span class="line"><span class="comment">     * Super 构造块</span></span><br><span class="line"><span class="comment">     * Super 构造方法</span></span><br><span class="line"><span class="comment">     * Sub 构造块</span></span><br><span class="line"><span class="comment">     * Sub 构造方法</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> args</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> Sub();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Super</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Super 静态块&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Super</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Super 构造方法&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Super 构造块&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sub</span> <span class="keyword">extends</span> <span class="title">Super</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Sub 静态块&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Sub</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Sub 构造方法&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Sub 构造块&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
</ul>
</li>
</ul>
<div class="note primary simple"><p>编译及反编译</p>
</div>
<ol>
<li><p>定义测试类，并使用javac 命令编译，产生 字节码文件</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JVMTest2</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String CONST_FIELD = <span class="string">&quot;AAA&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String staticField;</span><br><span class="line">    <span class="keyword">private</span> String field;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">add</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> staticField + field + CONST_FIELD;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> JVMTest2().add();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>使用 <code>javap -v -p JVMTest2 &gt;1.txt</code>将反编译后的文件输出到文件中(字节码指令可以参照指令表 )</p>
<figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br></pre></td><td class="code"><pre><span class="line">Classfile /C:/Users/wkq/Documents/gitee/java �ܹ�ʦ����/jvm/src/theory/JVMTest2.class</span><br><span class="line">  Last modified 2022-4-21; size 650 bytes</span><br><span class="line">  MD5 checksum 3292f4318baf0155784dced17532e95e</span><br><span class="line">  Compiled from &quot;JVMTest2.java&quot;</span><br><span class="line">public class theory.JVMTest2</span><br><span class="line">  minor version: 0</span><br><span class="line">  major version: 52</span><br><span class="line">  flags: ACC_PUBLIC, ACC_SUPER</span><br><span class="line">Constant pool:</span><br><span class="line">   #1 = Methodref          #12.#28        // java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">   #2 = Class              #29            // java/lang/StringBuilder</span><br><span class="line">   #3 = Methodref          #2.#28         // java/lang/StringBuilder.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">   #4 = Fieldref           #7.#30         // theory/JVMTest2.staticField:Ljava/lang/String;</span><br><span class="line">   #5 = Methodref          #2.#31         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;</span><br><span class="line">   #6 = Fieldref           #7.#32         // theory/JVMTest2.field:Ljava/lang/String;</span><br><span class="line">   #7 = Class              #33            // theory/JVMTest2</span><br><span class="line">   #8 = String             #34            // AAA</span><br><span class="line">   #9 = Methodref          #2.#35         // java/lang/StringBuilder.toString:()Ljava/lang/String;</span><br><span class="line">  #10 = Methodref          #7.#28         // theory/JVMTest2.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">  #11 = Methodref          #7.#36         // theory/JVMTest2.add:()Ljava/lang/String;</span><br><span class="line">  #12 = Class              #37            // java/lang/Object</span><br><span class="line">  #13 = Utf8               CONST_FIELD</span><br><span class="line">  #14 = Utf8               Ljava/lang/String;</span><br><span class="line">  #15 = Utf8               ConstantValue</span><br><span class="line">  #16 = Utf8               staticField</span><br><span class="line">  #17 = Utf8               field</span><br><span class="line">  #18 = Utf8               &lt;init&gt;</span><br><span class="line">  #19 = Utf8               ()V</span><br><span class="line">  #20 = Utf8               Code</span><br><span class="line">  #21 = Utf8               LineNumberTable</span><br><span class="line">  #22 = Utf8               add</span><br><span class="line">  #23 = Utf8               ()Ljava/lang/String;</span><br><span class="line">  #24 = Utf8               main</span><br><span class="line">  #25 = Utf8               ([Ljava/lang/String;)V</span><br><span class="line">  #26 = Utf8               SourceFile</span><br><span class="line">  #27 = Utf8               JVMTest2.java</span><br><span class="line">  #28 = NameAndType        #18:#19        // &quot;&lt;init&gt;&quot;:()V</span><br><span class="line">  #29 = Utf8               java/lang/StringBuilder</span><br><span class="line">  #30 = NameAndType        #16:#14        // staticField:Ljava/lang/String;</span><br><span class="line">  #31 = NameAndType        #38:#39        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;</span><br><span class="line">  #32 = NameAndType        #17:#14        // field:Ljava/lang/String;</span><br><span class="line">  #33 = Utf8               theory/JVMTest2</span><br><span class="line">  #34 = Utf8               AAA</span><br><span class="line">  #35 = NameAndType        #40:#23        // toString:()Ljava/lang/String;</span><br><span class="line">  #36 = NameAndType        #22:#23        // add:()Ljava/lang/String;</span><br><span class="line">  #37 = Utf8               java/lang/Object</span><br><span class="line">  #38 = Utf8               append</span><br><span class="line">  #39 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;</span><br><span class="line">  #40 = Utf8               toString</span><br><span class="line">&#123;</span><br><span class="line">  private static final java.lang.String CONST_FIELD;</span><br><span class="line">    descriptor: Ljava/lang/String;</span><br><span class="line">    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL</span><br><span class="line">    ConstantValue: String AAA</span><br><span class="line"></span><br><span class="line">  private static java.lang.String staticField;</span><br><span class="line">    descriptor: Ljava/lang/String;</span><br><span class="line">    flags: ACC_PRIVATE, ACC_STATIC</span><br><span class="line"></span><br><span class="line">  private java.lang.String field;</span><br><span class="line">    descriptor: Ljava/lang/String;</span><br><span class="line">    flags: ACC_PRIVATE</span><br><span class="line"></span><br><span class="line">  public theory.JVMTest2();</span><br><span class="line">    descriptor: ()V</span><br><span class="line">    flags: ACC_PUBLIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=1, locals=1, args_size=1</span><br><span class="line">         0: aload_0</span><br><span class="line">         1: invokespecial #1                  // Method java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">         4: return</span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line 3: 0</span><br><span class="line"></span><br><span class="line">  public java.lang.String add();</span><br><span class="line">    descriptor: ()Ljava/lang/String;</span><br><span class="line">    flags: ACC_PUBLIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=2, locals=1, args_size=1</span><br><span class="line">         0: new           #2                  // class java/lang/StringBuilder</span><br><span class="line">         3: dup</span><br><span class="line">         4: invokespecial #3                  // Method java/lang/StringBuilder.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">         7: getstatic     #4                  // Field staticField:Ljava/lang/String;</span><br><span class="line">        10: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;</span><br><span class="line">        13: aload_0</span><br><span class="line">        14: getfield      #6                  // Field field:Ljava/lang/String;</span><br><span class="line">        17: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;</span><br><span class="line">        20: ldc           #8                  // String AAA</span><br><span class="line">        22: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;</span><br><span class="line">        25: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;</span><br><span class="line">        28: areturn</span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line 9: 0</span><br><span class="line"></span><br><span class="line">  public static void main(java.lang.String[]);</span><br><span class="line">    descriptor: ([Ljava/lang/String;)V</span><br><span class="line">    flags: ACC_PUBLIC, ACC_STATIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=2, locals=1, args_size=1</span><br><span class="line">         0: new           #7                  // class theory/JVMTest2</span><br><span class="line">         3: dup</span><br><span class="line">         4: invokespecial #10                 // Method &quot;&lt;init&gt;&quot;:()V</span><br><span class="line">         7: invokevirtual #11                 // Method add:()Ljava/lang/String;</span><br><span class="line">        10: pop</span><br><span class="line">        11: return</span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line 13: 0</span><br><span class="line">        line 14: 11</span><br><span class="line">&#125;</span><br><span class="line">SourceFile: &quot;JVMTest2.java&quot;</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h2 id="编译器的优化"><a href="#编译器的优化" class="headerlink" title="编译器的优化"></a>编译器的优化</h2><p>众所周知，Java有两种运行模式（JVM会用编译的方式为代码提速）：</p>
<ul>
<li>解释执行：由解释器一行一行翻译执行<ul>
<li>优势在于没有编译的等待时间</li>
<li>性能相对差一些</li>
</ul>
</li>
<li>编译执行：把字节码编译成机器码，直接执行机器码<ul>
<li>运行效率会高很多，一般认为比解释执行快一个数量级</li>
<li>带来了额外的开销</li>
</ul>
</li>
</ul>
<div class="note info simple"><p>使用 Java -version 可以查看运行模式，mixed-mode 是混合模式，表示部分代码编译执行，部分代码解释执行。</p>
</div>
<ul>
<li>-Xint ： 设置 JVM  的执行模式为解释执行模式，如果想让springboot 项目按解释执行的话，只需要  java -Xint -jar  xxx.jar</li>
<li>-Xcomp : JVM 优先以 编译模式运行，不能编译的，以解释模式运行</li>
<li>-Xmixed ：混合模式运行，默认就是混合模式</li>
</ul>
<div class="note info simple"><p>一般情况下</p>
</div>
<ul>
<li>一开始一般由解释器解释执行</li>
<li>当虚拟机发现某个方法或代码块的运行特别频繁的时候，就会认为这些代码是“<strong>热点代码</strong>”。为了提高代码的执行效率，会用即时编译器（也就是JIT，just-in-time Compiler）把这些热点代码编译成与本地平台相关的机器码，<strong>并进行各层次的优化</strong>。</li>
</ul>
<div class="note info simple"><p>Hotspot的即时编译器-C1</p>
</div>
<ul>
<li>是一个简单快速的编译器</li>
<li>主要关注局部性的优化</li>
<li>适用于执行时间较短或对启动性能有要求的程序。例如，GUI 应用对界面启动速度就有一定要求。</li>
<li>也被称为是Client Compiler</li>
</ul>
<div class="note info simple"><p>Hotspot的即时编译器-C2</p>
</div>
<ul>
<li>是为长期运行的服务端应用程序做性能调优的编译器，比如SpringBoot程序</li>
<li>适用于执行时间较长或对峰值性能有要求的程序</li>
<li><p>也被称为是 Server Compiler</p>
<div class="note info simple"><p>总结：</p>
</div>
</li>
<li><p>Java 有解释执行和编译执行两种运行模式</p>
</li>
<li>Java 有两个即时编译器，C1 和 C2 ；C1编译器使用执行客户端程序，C2 编译器适合执行服务端程序</li>
</ul>
<h3 id="分层编译-各种层次的优化"><a href="#分层编译-各种层次的优化" class="headerlink" title="分层编译-各种层次的优化"></a>分层编译-各种层次的优化</h3><p>从 JDK 7开始引入了分层编译的概念 ，可以细分为五种编译级别：</p>
<ul>
<li>0 ： 解释执行</li>
<li>1 ： 简单C1编译：会用C1编译器进行一些简单的优化，不开启 Profiling （所谓的 Profiling 是 JVM 的性能监控）</li>
<li>2 :   受限的C1 编译：仅会执行带<strong>方法调用次数</strong>以及<strong>循环回边执行次数</strong> Profiling 的 C1 编译</li>
<li>3 ： 完全C1 编译：会执行所有带有 Profiling 的C1代码</li>
<li>4 ： C2 编译：使用C2 编译器进行优化，该级别会启用一些编译耗时较长的优化，一些情况下会根据性能监控信息进行一些非常激进的性能优化</li>
</ul>
<p>级别越高，应用启动越慢，优化的开销越高，峰值性能也越高</p>
<p>默认情况下，JDK 8 是开启分层编译的</p>
<ul>
<li>只想开启 C2： -XX:-TieredCompilation (禁止中间编译层（123层）)</li>
<li>只想开启C1：-XX:+TieredCompilation -XX:TieredStopAtLevel=1</li>
</ul>
<h3 id="如何找到热点代码？"><a href="#如何找到热点代码？" class="headerlink" title="如何找到热点代码？"></a>如何找到热点代码？</h3><p>编译执行主要是针对热点代码的，那么就需要找到热点代码</p>
<p>找到热点代码的思路有两种：</p>
<ul>
<li>基于采样的热点探测：周期检查各个线程的栈顶，如果发现某些方法老是出现在栈顶，说明这个方法是热点方法</li>
<li><strong>基于计数器的热点探测</strong>：为每一个方法甚至是代码块建立计数器，然后统计执行的次数，如果超过一定阈值就说明它是热点方法；</li>
</ul>
 <div class="note primary simple"><p>Hotspot虚拟机采用的是基于计数器的热点探测，Hotspot虚拟机内置的两类计数器：</p>
</div>
<ul>
<li><p>方法调用计数器（Invocation Counter）： </p>
<p><img src= "" data-lazy-src="方法调用计数器的执行流程.png" alt=""></p>
<ul>
<li><p>用于统计方法被调用的次数，在不开启分层编译的情况下，在C1 编译器下的默认阈值时1500次，在C2模式下是10000次。也可以用 -XX:CompileThreshold=X 指定阈值</p>
</li>
<li><p>如果不做任何设置，方法调用计数器统计的并不是方法被调用的绝对次数，而是一个相对的执行频率，即一段时间之内方法被调用的次数。当超过一定的时间限度，如果方法的调用次数仍然不足以让它提交给即时编译器，那这个方法的调用计数器的计数值就会减少一半，这个过程称为方法调用计数器热度的衰减，而这段时间就被成此方法统计的半衰周期。进行热度衰减的动作是在虚拟机进行垃圾收集时顺便进行的，可以使用虚拟机参数 -XX:-UseCounterDecay 来关闭热度衰减，让方法计数器统计方法调用的绝对次数，这样，只要系统运行的时间足够长，绝大部分方法都会被编译成本地代码。另外，可以使用 -XX:CounterHalfLifeTime参数设置半衰周期的时间，单位是秒</p>
</li>
</ul>
</li>
<li><p>回边计数器（Back Edge Counter）</p>
<p><img src= "" data-lazy-src="回边计数器的执行流程.png" alt=""></p>
<ul>
<li>用于统计一个方法中循环体代码执行的次数，在字节码中遇到控制流向后跳转的指令叫做“回边“（Back Edge），在不开启分层编译的情况下，C1 编译器下的默认阈值 13995，C2 默认为10700，可使用 -XX:OnStackReplacePercentage=X 指定阈值</li>
<li>建立回边计数器的主要目的是为了触发OSR（OnStackReplacement）编译，是用来在运行时替换正在运行的函数的栈帧的技术</li>
</ul>
</li>
</ul>
<p>当开启分层编译时，JVM会根据当前编译的方法数以及编译线程数来动态调整阈值，-XX:CompileThreshold、-XX:OnStackReplacePercentage 都会失效。</p>
 <div class="note primary simple"><p>上面出现的JVM参数统计：</p>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>参数</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>-Xmixed</td>
<td>混合模式运行（默认）</td>
</tr>
<tr>
<td>-Xint</td>
<td>设置JVM的执行模式为解释执行模式</td>
</tr>
<tr>
<td>-XComp</td>
<td>JVM优先以编译模式运行，不能编译的，以解释模式运行。</td>
</tr>
<tr>
<td>-XX:-TieredCompilation</td>
<td>禁用中间编译层</td>
</tr>
<tr>
<td>-XX:-TiredStopAtLevel</td>
<td>到那个分层停止</td>
</tr>
<tr>
<td>-XX:CompileThreshold=X</td>
<td>指定方法调用计数器阈值（关闭分层编译才有效果）</td>
</tr>
<tr>
<td>-XX:OnStackReplacePercentage=X</td>
<td>指定回边计数器阈值（关闭分层编译时才有效）</td>
</tr>
<tr>
<td>-XX:UseCounterDecay</td>
<td>关闭方法调用计数器热度衰减</td>
</tr>
<tr>
<td>-XX:CounterHalfLifeTime</td>
<td>指定方法调用计数器半衰周期</td>
</tr>
</tbody>
</table>
</div>
<h2 id="JIT优化措施—方法内联"><a href="#JIT优化措施—方法内联" class="headerlink" title="JIT优化措施—方法内联"></a>JIT优化措施—方法内联</h2><p>JVM 会把目标方法的代码复制到发起调用的方法之中，避免发生真实的方法调用</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">InlineTest1</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 调用方法会进行压栈和出栈的操作，进入方法的时候会向栈里面压入一个元素，return 会从栈里面弹出一个元素，压栈和出栈操作都是有开销的</span></span><br><span class="line"><span class="comment">     * 压栈存数据存在内存的开销，压栈出栈都是需要时间的，所以还有时间的开销，如果这段代码的调用次数不多，压栈和出栈的开销也就无所谓了</span></span><br><span class="line"><span class="comment">     * 但如果这段代码调用的频次非常高，那么累积下来的开销还是非常可观的。</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;</span></span><br><span class="line"><span class="comment">     * JVM 会自动识别热点方法，并自动的进行方法内联，所谓的方法内联就是把目标方法的代码复制到发起调用的方法里面去，避免发生真实的方法调用</span></span><br><span class="line"><span class="comment">     * return add2(x1, x2) + add2(x3, x4); 经过方法内联之后就变为了  return x1+x2+x3+x4  ;从而减少掉压栈和出栈的操作</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * 使用方法内联是有条件的</span></span><br><span class="line"><span class="comment">     * 1. 方法体足够小，</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> x1</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> x2</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> x3</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> x4</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">add1</span><span class="params">(<span class="keyword">int</span> x1, <span class="keyword">int</span> x2, <span class="keyword">int</span> x3, <span class="keyword">int</span> x4)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> add2(x1, x2) + add2(x3, x4);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">add2</span><span class="params">(<span class="keyword">int</span> x1, <span class="keyword">int</span> x2)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> x1 + x2;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
 <div class="note primary simple"><p>方法内联的条件1：方法体足够小</p>
</div>
<ul>
<li><p>热点方法：如果方法体小于325字节会尝试内联，可用 -XX:FreqInlineSize 修改大小</p>
</li>
<li><p>非热点方法：如果方法体小于35字节，会尝试内联，可用-XX:MaxInlineSize 修改大小</p>
<div class="note primary simple"><p>方法内联的条件2：被调用方法运行时的实现被可以唯一确定</p>
</div>
</li>
<li><p>static 方法、private方法及final方法，JIT可以唯一确定具体的实现代码</p>
</li>
<li><p>public 的实例方法，指向的实现可能是自身、父类、子类的代码，当且仅当JIT能够唯一确定方法的具体实现时，才有可能完成内联。</p>
<div class="note simple simple"><p>方法内联注意点：</p>
</div>
</li>
<li><p>尽量让方法体小一些</p>
</li>
<li>尽量使用 final、private、static关键字修饰方法，避免因为多态，需要对方法做额外的检查</li>
<li><p>一些场景下，可以通过设置 JVM 参数修改阈值，从而让更多方法内联</p>
<div class="note primary simple"><p>内联可能带来的问题：</p>
</div>
</li>
</ul>
<p>内联本质上是用空间换时间 ，也就是即时编译期间把方法调用之前连接起来，从而减少入栈和出栈的开销。但是，经过内联之后的代码会变多，增加的代码量又取决于方法的调用次数以及方法本身的大小，所以在一些极端场景下，内联会导致：</p>
<ul>
<li>CodeCache（CodeCache是热点代码的缓冲区，即时编译器编译过的代码以及本地方法代码都会存放在CodeCache里面 ，这块空间是比较有限的，JDK8 默认只有240M ）的溢出，导致JVM退化成解释执行模式。</li>
</ul>
<h2 id="逃逸分析、标量替换、栈上分配"><a href="#逃逸分析、标量替换、栈上分配" class="headerlink" title="逃逸分析、标量替换、栈上分配"></a>逃逸分析、标量替换、栈上分配</h2><h3 id="逃逸分析"><a href="#逃逸分析" class="headerlink" title="逃逸分析"></a>逃逸分析</h3><p>分析变量能否逃出它的作用域</p>
<ul>
<li>全局变量赋值逃逸</li>
<li>方法返回值逃逸</li>
<li>实例引用逃逸</li>
<li>线程逃逸 ：赋值给类变量或可以在其它线程中可以访问的实例变量</li>
</ul>
<p>以上是逃逸分析的四种场景，JVM在做逃逸分析的时候会针对这些场景进行分析，分析完成之后会为对象做一个逃逸状态标记，一个对象主要有三种逃逸状态标记：</p>
<ul>
<li>全局级别逃逸：一个对象可能从方法或者当前线程中逃逸，其它的方法或者线程也可以访问到这个对象<ul>
<li>对象作为方法的返回值返回</li>
<li>对象作为静态字段（static field）或者成员变量 （field） </li>
<li>如果重写了某个类的 finalize（）方法，那么这个类的对象都会被标记为全局逃逸状态并且一定会放在堆内存中。</li>
</ul>
</li>
<li>参数级别逃逸：对象作为参数传递给一个方法，但是这个方法之外无法访问/对其它线程不可见</li>
<li><p>无逃逸： 一个对象不会逃逸</p>
<div class="note primary simple"><p>逃逸分析代码示例：</p>
</div>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EscapeTest1</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> SomeClass someClass;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 全局变量赋值逃逸</span></span><br><span class="line"><span class="comment">     * 将局部变量赋值给了静态变量，局部变量的作用域是在方法内部，类变量的作用域是在内里面</span></span><br><span class="line"><span class="comment">     * 作用域被方法了，显然发生了逃逸</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">globalVariablePointerEscape</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        someClass = <span class="keyword">new</span> SomeClass();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 方法返回值逃逸</span></span><br><span class="line"><span class="comment">     * someMethod()&#123;</span></span><br><span class="line"><span class="comment">     * SomeClass someClass = methodPointerEscape();</span></span><br><span class="line"><span class="comment">     * &#125;</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> SomeClass <span class="title">methodPointerEscape</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> SomeClass();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 实例引用传播逃逸</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">instancePassPointerEscape</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.methodPointerEscape().printClassName(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 如果开启了标量替换，并不会创建 SomeTest的实例，而是会创建 someTest 的成员变量区代替</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">someTest</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        SomeTest someTest = <span class="keyword">new</span> SomeTest();</span><br><span class="line">        someTest.age = <span class="number">1</span>;</span><br><span class="line">        someTest.id = <span class="number">1</span>;</span><br><span class="line">        <span class="comment">//开启标量替换之后,</span></span><br><span class="line">        <span class="keyword">int</span> age = <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">int</span> id = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SomeClass</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">printClassName</span><span class="params">(EscapeTest1 escapeTest1)</span> </span>&#123;</span><br><span class="line">        System.out.println(escapeTest1.getClass().getName());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SomeTest</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> id;</span><br><span class="line">    <span class="keyword">int</span> age;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="标量替换"><a href="#标量替换" class="headerlink" title="标量替换"></a>标量替换</h3> <div class="note primary simple"><p>标量和聚合量：</p>
</div>
<p>所谓的标量是指不能进一步分解的量 </p>
<ul>
<li>基础数据类型</li>
<li>对象引用</li>
</ul>
<p>和标量对应的是聚合量，聚合量是可以进一步分解的量</p>
<ul>
<li>字符串：用字节数组实现</li>
<li>自定义的变量等</li>
</ul>
<div class="note primary simple"><p>标量替换：</p>
</div>
<ul>
<li>通过逃逸分析确定该对象不会被外部访问，并且对象可以被进一步分解时，JVM 不会创建该对象，而是创建它  的成员变量来代替。（把聚合量用若干个标量代替，从而节省内存）</li>
<li>-XX:+EliminteAllocations 开启标量替换（JDK 8 默认开启）</li>
</ul>
<h3 id="栈上分配"><a href="#栈上分配" class="headerlink" title="栈上分配"></a>栈上分配</h3><p>Java 里面绝大多数对象都是存放在堆里面的，当对象不再使用的时候，靠垃圾回收器去回收对象</p>
<p>栈上分配是指 ，通过逃逸分析，能够确认对象不会被外部访问，就在栈上分配对象。在栈上分配对象，这个对象占用的空间就可以在栈帧出栈的时候被销毁了。通过栈上分配可以降低垃圾回收的压力。</p>
<div class="note primary simple"><p>相关JVM参数：</p>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>参数</th>
<th>默认值（JDK8）</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>-XX:+DoEscapeAnalysis</td>
<td>开启</td>
<td>是否开启逃逸分析</td>
</tr>
<tr>
<td>-XX:+EliminateAllocations</td>
<td>开启</td>
<td>是否开启标量替换</td>
</tr>
<tr>
<td>-XX:+EliminateLocks</td>
<td>开启</td>
<td>是否开启锁消除</td>
</tr>
</tbody>
</table>
</div>
</article><div class="tag_share"><div class="post-meta__tag-list"></div><div class="post_share"><div class="social-share" data-image="/media/top_img.jpg" data-sites="wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/social-share.js/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/social-share.js/dist/js/social-share.min.js" defer></script></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src= "" data-lazy-src="/media/avatar.jpg" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">王开琦</div><div class="author-info__description">今日事今日毕</div></div><div class="card-info-data"><div class="card-info-data-item is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">15</div></a></div><div class="card-info-data-item is-center"><a href="/tags/"><div class="headline">标签</div><div class="length-num">17</div></a></div><div class="card-info-data-item is-center"><a href="/categories/"><div class="headline">分类</div><div class="length-num">15</div></a></div></div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84"><span class="toc-number">1.</span> <span class="toc-text">JVM 内存结构</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A0%86"><span class="toc-number">1.1.</span> <span class="toc-text">堆</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88"><span class="toc-number">1.2.</span> <span class="toc-text">虚拟机栈</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%B3%95%E6%A0%88"><span class="toc-number">1.3.</span> <span class="toc-text">本地方法栈</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8"><span class="toc-number">1.4.</span> <span class="toc-text">程序计数器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E5%8C%BA"><span class="toc-number">1.5.</span> <span class="toc-text">方法区</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B%E8%AF%A6%E8%A7%A3"><span class="toc-number">2.</span> <span class="toc-text">类加载过程详解</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%96%E8%AF%91%E5%99%A8%E7%9A%84%E4%BC%98%E5%8C%96"><span class="toc-number">3.</span> <span class="toc-text">编译器的优化</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%86%E5%B1%82%E7%BC%96%E8%AF%91-%E5%90%84%E7%A7%8D%E5%B1%82%E6%AC%A1%E7%9A%84%E4%BC%98%E5%8C%96"><span class="toc-number">3.1.</span> <span class="toc-text">分层编译-各种层次的优化</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A6%82%E4%BD%95%E6%89%BE%E5%88%B0%E7%83%AD%E7%82%B9%E4%BB%A3%E7%A0%81%EF%BC%9F"><span class="toc-number">3.2.</span> <span class="toc-text">如何找到热点代码？</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#JIT%E4%BC%98%E5%8C%96%E6%8E%AA%E6%96%BD%E2%80%94%E6%96%B9%E6%B3%95%E5%86%85%E8%81%94"><span class="toc-number">4.</span> <span class="toc-text">JIT优化措施—方法内联</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90%E3%80%81%E6%A0%87%E9%87%8F%E6%9B%BF%E6%8D%A2%E3%80%81%E6%A0%88%E4%B8%8A%E5%88%86%E9%85%8D"><span class="toc-number">5.</span> <span class="toc-text">逃逸分析、标量替换、栈上分配</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90"><span class="toc-number">5.1.</span> <span class="toc-text">逃逸分析</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A0%87%E9%87%8F%E6%9B%BF%E6%8D%A2"><span class="toc-number">5.2.</span> <span class="toc-text">标量替换</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A0%88%E4%B8%8A%E5%88%86%E9%85%8D"><span class="toc-number">5.3.</span> <span class="toc-text">栈上分配</span></a></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/blog/3-%E6%95%B0%E6%8D%AE%E5%BA%93-%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9B%91%E6%8E%A7%E4%B8%8E%E8%B0%83%E4%BC%98-%E7%89%B9%E5%AE%9A%E8%AF%AD%E5%8F%A5%E7%9A%84%E5%8E%9F%E7%90%86%E5%92%8C%E8%B0%83%E4%BC%98/" title="3.数据库-数据库监控与调优-特定语句的原理和调优"><img src= "" data-lazy-src="/media/top_img.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="3.数据库-数据库监控与调优-特定语句的原理和调优"/></a><div class="content"><a class="title" href="/blog/3-%E6%95%B0%E6%8D%AE%E5%BA%93-%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9B%91%E6%8E%A7%E4%B8%8E%E8%B0%83%E4%BC%98-%E7%89%B9%E5%AE%9A%E8%AF%AD%E5%8F%A5%E7%9A%84%E5%8E%9F%E7%90%86%E5%92%8C%E8%B0%83%E4%BC%98/" title="3.数据库-数据库监控与调优-特定语句的原理和调优">3.数据库-数据库监控与调优-特定语句的原理和调优</a><time datetime="2022-04-22T08:14:26.000Z" title="发表于 2022-04-22 16:14:26">2022-04-22</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/blog/3-%E6%95%B0%E6%8D%AE%E5%BA%93-%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9B%91%E6%8E%A7%E4%B8%8E%E8%B0%83%E4%BC%98-%E6%95%B0%E6%8D%AE%E5%BA%93%E8%B0%83%E4%BC%98%E7%90%86%E8%AE%BA/" title="数据库调优理论"><img src= "" data-lazy-src="/media/top_img.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="数据库调优理论"/></a><div class="content"><a class="title" href="/blog/3-%E6%95%B0%E6%8D%AE%E5%BA%93-%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9B%91%E6%8E%A7%E4%B8%8E%E8%B0%83%E4%BC%98-%E6%95%B0%E6%8D%AE%E5%BA%93%E8%B0%83%E4%BC%98%E7%90%86%E8%AE%BA/" title="数据库调优理论">数据库调优理论</a><time datetime="2022-04-22T08:13:31.000Z" title="发表于 2022-04-22 16:13:31">2022-04-22</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/blog/3-%E6%95%B0%E6%8D%AE%E5%BA%93-%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9B%91%E6%8E%A7%E4%B8%8E%E8%B0%83%E4%BC%98-%E6%85%A2%E6%9F%A5%E8%AF%A2%E5%8F%91%E7%8E%B0%E4%B8%8E%E5%88%86%E6%9E%90/" title="慢查询发现与分析"><img src= "" data-lazy-src="/media/top_img.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="慢查询发现与分析"/></a><div class="content"><a class="title" href="/blog/3-%E6%95%B0%E6%8D%AE%E5%BA%93-%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9B%91%E6%8E%A7%E4%B8%8E%E8%B0%83%E4%BC%98-%E6%85%A2%E6%9F%A5%E8%AF%A2%E5%8F%91%E7%8E%B0%E4%B8%8E%E5%88%86%E6%9E%90/" title="慢查询发现与分析">慢查询发现与分析</a><time datetime="2022-04-22T08:12:32.000Z" title="发表于 2022-04-22 16:12:32">2022-04-22</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/blog/1-Java-JVM%E8%B0%83%E4%BC%98%E7%90%86%E8%AE%BA/" title="JVM 调优理论"><img src= "" data-lazy-src="/media/top_img.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="JVM 调优理论"/></a><div class="content"><a class="title" href="/blog/1-Java-JVM%E8%B0%83%E4%BC%98%E7%90%86%E8%AE%BA/" title="JVM 调优理论">JVM 调优理论</a><time datetime="2022-04-21T06:40:41.000Z" title="发表于 2022-04-21 14:40:41">2022-04-21</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/blog/20-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84-%E6%9C%8D%E5%8A%A1%E6%B2%BB%E7%90%86-%E6%9C%8D%E5%8A%A1%E6%B2%BB%E7%90%86Eureka/" title="服务治理Eureka"><img src= "" data-lazy-src="/media/top_img.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="服务治理Eureka"/></a><div class="content"><a class="title" href="/blog/20-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84-%E6%9C%8D%E5%8A%A1%E6%B2%BB%E7%90%86-%E6%9C%8D%E5%8A%A1%E6%B2%BB%E7%90%86Eureka/" title="服务治理Eureka">服务治理Eureka</a><time datetime="2022-04-13T13:18:27.000Z" title="发表于 2022-04-13 21:18:27">2022-04-13</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="footer_custom_text">Copyright© 2022 blog.wkq.pub <span>陇ICP备2021003596号</span></a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">繁</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><div class="search-dialog__title" id="local-search-title">本地搜索</div><div id="local-input-panel"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div></div><hr/><div id="local-search-results"></div><span class="search-close-button"><i class="fas fa-times"></i></span></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js"></script><script src="/js/search/local-search.js"></script><script>var preloader = {
  endLoading: () => {
    document.body.style.overflow = 'auto';
    document.getElementById('loading-box').classList.add("loaded")
  },
  initLoading: () => {
    document.body.style.overflow = '';
    document.getElementById('loading-box').classList.remove("loaded")

  }
}
window.addEventListener('load',preloader.endLoading())</script><div class="js-pjax"><script>if (document.getElementsByClassName('mermaid').length) {
  if (window.mermaidJsLoad) mermaid.init()
  else {
    getScript('https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js').then(() => {
      window.mermaidJsLoad = true
      mermaid.initialize({
        theme: 'default',
      })
      true && mermaid.init()
    })
  }
}</script></div><div class="aplayer no-destroy" data-id="7675104902" data-server="tencent" data-type="playlist" data-fixed="true" data-mini="true" data-listFolded="false" data-order="random" data-preload="none" data-autoplay="true" muted></div><canvas class="fireworks" mobile="true"></canvas><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/fireworks.min.js"></script><script defer="defer" id="fluttering_ribbon" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-fluttering-ribbon.min.js"></script><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script><script src="https://cdn.jsdelivr.net/gh/metowolf/MetingJS@1.2/dist/Meting.min.js"></script><script src="https://cdn.jsdelivr.net/npm/pjax/pjax.min.js"></script><script>let pjaxSelectors = [
  'title',
  '#config-diff',
  '#body-wrap',
  '#rightside-config-hide',
  '#rightside-config-show',
  '.js-pjax'
]

if (false) {
  pjaxSelectors.unshift('meta[property="og:image"]', 'meta[property="og:title"]', 'meta[property="og:url"]')
}

var pjax = new Pjax({
  elements: 'a:not([target="_blank"])',
  selectors: pjaxSelectors,
  cacheBust: false,
  analytics: false,
  scrollRestoration: false
})

document.addEventListener('pjax:send', function () {

  // removeEventListener scroll 
  window.removeEventListener('scroll', window.tocScrollFn)
  window.removeEventListener('scroll', scrollCollect)

  typeof preloader === 'object' && preloader.initLoading()
  
  if (window.aplayers) {
    for (let i = 0; i < window.aplayers.length; i++) {
      if (!window.aplayers[i].options.fixed) {
        window.aplayers[i].destroy()
      }
    }
  }

  typeof typed === 'object' && typed.destroy()

  //reset readmode
  const $bodyClassList = document.body.classList
  $bodyClassList.contains('read-mode') && $bodyClassList.remove('read-mode')

})

document.addEventListener('pjax:complete', function () {
  window.refreshFn()

  document.querySelectorAll('script[data-pjax]').forEach(item => {
    const newScript = document.createElement('script')
    const content = item.text || item.textContent || item.innerHTML || ""
    Array.from(item.attributes).forEach(attr => newScript.setAttribute(attr.name, attr.value))
    newScript.appendChild(document.createTextNode(content))
    item.parentNode.replaceChild(newScript, item)
  })

  GLOBAL_CONFIG.islazyload && window.lazyLoadInstance.update()

  typeof chatBtnFn === 'function' && chatBtnFn()
  typeof panguInit === 'function' && panguInit()

  // google analytics
  typeof gtag === 'function' && gtag('config', '', {'page_path': window.location.pathname});

  // baidu analytics
  typeof _hmt === 'object' && _hmt.push(['_trackPageview',window.location.pathname]);

  typeof loadMeting === 'function' && document.getElementsByClassName('aplayer').length && loadMeting()

  // Analytics
  if (false) {
    MtaH5.pgv()
  }

  // prismjs
  typeof Prism === 'object' && Prism.highlightAll()

  typeof preloader === 'object' && preloader.endLoading()
})

document.addEventListener('pjax:error', (e) => {
  if (e.request.status === 404) {
    pjax.loadUrl('/404.html')
  }
})</script></div><script src="/live2d_models/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"pluginRootPath":"live2d_models/","pluginJsPath":"lib/","pluginModelPath":"assets/","tagMode":false,"debug":false,"model":{"jsonPath":"/live2d_models/assets/penchan.model.json"},"display":{"position":"left","width":100,"height":150,"hOffset":-10,"vOffset":40},"mobile":{"show":true},"react":{"opacity":0.7},"log":false});</script></body></html>